#!/usr/bin/env python
# -*- coding: utf-8 -*-
#
#       birthday-aware.py
#       
#       Copyright 2011 dave <eunbolt@gmail.com>
#       
#       This program is free software; you can redistribute it and/or modify
#       it under the terms of the GNU General Public License as published by
#       the Free Software Foundation; either version 2 of the License, or
#       (at your option) any later version.
#       
#       This program is distributed in the hope that it will be useful,
#       but WITHOUT ANY WARRANTY; without even the implied warranty of
#       MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
#       GNU General Public License for more details.
#       
#       You should have received a copy of the GNU General Public License
#       along with this program; if not, write to the Free Software
#       Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
#       MA 02110-1301, USA.
#

import pygtk
pygtk.require('2.0')
import gtk
import glib
import gobject
import pynotify
import appindicator
import fileinput
#lets us do things with the terminal
import os
#regex
import re
#this converts files to unicode
import codecs
import time
import string
import subprocess
import threading
#this lets us get today's date
import datetime
import calendar

gtk.gdk.threads_init()#do we needs this?

#get the last day of the current month
last_day_of_current_month = calendar.mdays[datetime.date.today().month] 

#we use this to rehide the indicator when it is not needed
#global
menu_item_counter = 0
todays_date = re.split('[\-]', str(datetime.date.today()))
menu = gtk.Menu()
ind = appindicator.Indicator ("birthday indicator","birthday-aware", appindicator.CATEGORY_APPLICATION_STATUS)



def current_date():
	"""update the todays_date variable so that the days until birthday change at midnight"""
	global todays_date
	print todays_date
	todays_date = 0
	print "should be zero underneath"
	print todays_date
	#Get todays date and put it in an array [0]=year [1]=month [2]=day
	todays_date = re.split('[\-]', str(datetime.date.today()))
	print "checking the birthday list again, because today is a new day"
	print todays_date

def time_to_sleep():
	"""Find out when the next day begins"""
	startup_hour = time.localtime()[3] #the hour that is was when this program started
	startup_minute = time.localtime()[4] # the minutes thatit was when the program started
	minus_hour = 23 - startup_hour #we start it at 23 because we need to account for the minutes aswell
	minus_minutes = 60 - startup_minute
	hour_to_sec = minus_hour * 3600 #1 hour = 3600 seconds
	min_to_sec = minus_minutes * 60 #1 minute = 60 seconds
	time_to_midnight = hour_to_sec + min_to_sec # add the hour seconds and minute seconds together
	current_time = str(startup_hour) + ":" + str(startup_minute) # for testing purposes
	midnight = str(minus_hour) + ":" + str(minus_minutes) #testing purposes aswell
	print "------time--------"
	print "the current time = " + str(current_time)
	print "time until midnight = " + str(midnight)
	print "seconds until midnight = " + str(time_to_midnight)
	#time_to_midnight=20 #only uncomment to test looping the program for when the day changes
	return time_to_midnight
	
def send_notification(name, birthdate):
	"""send new updates to notify-osd"""
	#send a notification
	pynotify.init(name)
	imageURI = '/usr/share/icons/oxygen/32x32/actions/view-calendar-birthday.png'
	#						  title, info, image
	n = pynotify.Notification(str(name), str(birthdate), imageURI)
	n.set_hint_string("x-canonical-append","")# this mergers same site posts but affects the second site making it not show :( why?
	n.show()

def get_age(birthyear):
	"""Figure out how old they will be"""
	years_since_born = 0
	years_since_born = int(birthyear) - int(todays_date[0]) #figure out how old the person is
	birthday_age = years_since_born + 1 #add 1 to thier age, this will be used for saying how old they will be on thier birthday
	return abs(years_since_born), abs(birthday_age)

def check_birthday_month(birthday):
	"""Figure out if their birthday is this month or next month"""
	this_month = str(todays_date[1]) #this is the current month
	next_month = int(todays_date[1]) + 01 # this is next month
	is_it_near = "?"
	if str(birthday) in str(this_month): # if they are born this month
		is_it_near = "Their birthday is this month!"
	elif int(birthday) == next_month: # if they are born next month, this is usefull when do look for a person's birthday at the very begining of the next month
		is_it_near = "Their birthday is next month"
	else: # just print what month the were born in, I don't think we need this later
		is_it_near = "They were born in the " + str(birthday) + "th month" # i can't be bothered puting #st, #nd, #rd
	return is_it_near
	
def convert_number_to_words(input):
	"""turn the days until birthday into words"""
	if input == 0:
		output = "today."
	elif input == 1:
		output = "tomorrow."
	elif input == 3:
		output = "in 3 days."
	elif input == 7:
		output = "in 7 days."
	else:
		output = "wrong number inputed for function convert_number_to_words"
	return output
	
def check_birthday_coming_up(month, age_to_be, day):
	"""Check how many days away their birthday is"""
	this_month = str(todays_date[1]) # this is the current month
	next_month = int(todays_date[1]) + 01 # this is next month
	re = 0
	ra = ""
	if str(month) in str(this_month):
		if int(day) >= int(todays_date[2]): # if the day of their birthday is greater or equal to today's date
			days_until_birthday = int(day) - int(todays_date[2])# find out how many days until their birthday
			if days_until_birthday == 0: #Don't forget to check that their birthday is today!
				re = "Has a birthday " + str(convert_number_to_words(0)) + " Wish them a happy birthday. They are turning " + str(age_to_be) + "."
				ra = str(convert_number_to_words(0)) 
			elif days_until_birthday == 1: #check for tomorow
				#print (persons_name_array[1] + " has a birthday tomorrow!. They will be " + str(birthday_age) + " years old")
				re = "Has a birthday " + str(convert_number_to_words(1)) + " I hope you have a present for them. They are turning " + str(age_to_be) + "."
				ra = str(convert_number_to_words(1)) 
			elif days_until_birthday == 3:
				#print (persons_name_array[1] + " has a birthday in " + str(days_until_birthday) + " days. They will be " + str(birthday_age) + " years old")
				re = "Has a birthday " + str(convert_number_to_words(3)) + " They are turning " + str(age_to_be) + "."
				ra = str(convert_number_to_words(3)) 
			elif days_until_birthday == 7:
				#print (persons_name_array[1] + " has a birthday in " + str(days_until_birthday) + " days. They will be " + str(birthday_age) + " years old")
				re = "Has a birthday " + str(convert_number_to_words(7)) + " They are turning " + str(age_to_be) + "."
				ra = str(convert_number_to_words(7)) 
			else:
				#re "days until their birthday is not 7,3,1 or today"
				#re = "days until birthday = " + str(days_until_birthday)
				re = 0
	elif int(month) == next_month: #much of this is sorta duplicates of just above, I should have put it into a function
		#test_day = 30
		#test_birthday = 2
		#if test_birthday < 8: # if the day of their birthday is greater than today's date
		if int(day) < 8: # if the day of their birthday is greater than today's date
			#days_in_current_month_and_birthday = int(last_day_of_current_month) + int(test_day)
			days_in_current_month_and_birthday = int(last_day_of_current_month) + int(todays_date[2])
			days_until_birthday = int(days_in_current_month_and_birthday) - int(test_birthday)# find out how many days until their birthday
			#days_until_birthday = int(days_in_current_month_and_birthday) - int(day)# find out how many days until their birthday
			if days_until_birthday == 0: #Don't forget to check that thier birthday is today!
				re = "Has a birthday " + str(convert_number_to_words(0)) + " Wish them a happy birthday. They are turning " + str(age_to_be) + "."
				ra = str(convert_number_to_words(0))
			elif days_until_birthday == 1: #check for tomorow
				#print (persons_name_array[1] + " has a birthday tomorrow!. They will be " + str(birthday_age) + " years old")
				re = "Has a birthday " + str(convert_number_to_words(1)) + " I hope you have a present for them. They are turning " + str(age_to_be) + "."
				ra = str(convert_number_to_words(1))
			elif days_until_birthday == 3:
				#print (persons_name_array[1] + " has a birthday in " + str(days_until_birthday) + " days. They will be " + str(birthday_age) + " years old")
				re = "Has a birthday " + str(convert_number_to_words(3)) + " They are turning " + str(age_to_be) + "."
				ra = str(convert_number_to_words(3))
			elif days_until_birthday == 7:
				#print (persons_name_array[1] + " has a birthday in " + str(days_until_birthday) + " days. They will be " + str(birthday_age) + " years old")
				re = "Has a birthday " + str(convert_number_to_words(7)) + " They are turning " + str(age_to_be) + "."
				ra = str(convert_number_to_words(7))
			else:
				#re "days until their birthday is not 7,3,1 or today"
				#re = "days until birthday = " + str(days_until_birthday)
				re = 0
		re = 0
	else:
		re = 0
	return re, ra
	
def menuitem_response(birthday):
	"""remove the item from the birthday-indicator menu"""
	global menu_item_counter
	birthday.hide()
	menu_item_counter = menu_item_counter - 1
	print menu_item_counter
	if menu_item_counter == 0:
		ind.set_status (appindicator.STATUS_PASSIVE)#hide the indicator when we don't need it anymore
	
def create_menu_item(name, ttbirthday):
	"""Create a item in the birthday-indicator menu"""
	global menu_item_counter
	print ttbirthday
	ind.set_status (appindicator.STATUS_ATTENTION)#this need to be ATTENTION
	bday = gtk.MenuItem(str("Birthday for " + str(name) + " " + ttbirthday))
	menu.append(bday)
	bday.show()
	print menu_item_counter
	menu_item_counter = menu_item_counter + 1
	print menu_item_counter
	bday.connect("activate", lambda *a: menuitem_response(bday))

def split_on_parts(s, *parts): # not in use right now, was for yahoo, but yahoo fucked up their contact exports
    total = 0
    buildstr = []
    for p in parts:
        buildstr.append(s[total:total+p])
        total += p
    print buildstr
    total = 0
    return buildstr


def read_the_birthday_file():
	"""open the contact file and do stuff with the output"""
	#if os.path.exists(os.path.expanduser("~/.birthday-aware/") + 'WLMcontacts.cvs):#------------------FIXME
	#	print "mail.live"
	#if os.path.exists(os.path.expanduser("~/.birthday-aware/") + 'contact.vcf'):#------------------FIXME
	print "contacts.vcf"
	birthday_file = open(os.path.expanduser("~/.birthday-aware/") + 'contacts.vcf')
	for line in birthday_file: #for everyline in the file...
		if re.match("FN", line):  # match the word "FN" this let's us find the person's name
			print "-------------"
			persons_name_array = re.split('[:+\ ]', line)#gmail and??
		if re.match("BDAY", line):  # match the word "BDAY" this lets us find the person's birthdate
			birthdate = re.split('[:+\-]', line)#gmail and??  #birthdate[1]=year birthdate[2]=month birthdate[3]=day
			try:
				print persons_name_array[1] + ":" + birthdate[1] + "/" + birthdate[2] + "/" + birthdate[3]#make sure we read the correct infor from the file
				returned_age = get_age(int(birthdate[1])) # get the persons age and how old they will be on thier next birthday
			except:
				birthdate[1] = "Unknown year of birth"
				print str(persons_name_array[1]) + ":" + str(birthdate[1]) + "/" + str(birthdate[2]) + "/" + str(birthdate[3])#make sure we read the correct infor from the file
				returned_age = "?"
			#print str(persons_name_array[1]), " is ", str(returned_age [1]), " years old" #print years since borh ("abs()| converts the negative number into a positive
			#print "On their next birthday, they will be " + str(returned_age [0]) # this is the age the person will become		
			#print check_birthday_month(birthdate[2])#check when their birthday is (right now it's only used for testing?)
			birthday_soon_result = check_birthday_coming_up(birthdate[2], returned_age[0], birthdate[3])[0]# the full text about when thier birthday is
			birthday_soon_result_text = check_birthday_coming_up(birthdate[2], returned_age[0], birthdate[3])[1]# short text, e.g tomorrow, today, in 3 days
			if birthday_soon_result == 0:
				print "Their birthday is too far away to worry about"
			else:
				send_notification(persons_name_array[1], birthday_soon_result) # send a notification to notify-osd to
				print "notification to notify-osd sent"
				create_menu_item(persons_name_array[1], birthday_soon_result_text)
				print birthday_soon_result
	birthday_file.close()
	return True
	
def wait_for_midnight():
	"""Make the program sleep until the next day and repeat"""
	print "sleeping until midnight"
	#time_to_sleep = time_to_sleep + 30 #should we wait till 30seconds after midnight or do it straight on midnight?
	time.sleep(time_to_sleep())#sleep until midnight
	current_date() #set the new date before reading the contact list again
	read_the_birthday_file()	
	print "waiting until next midnight"
	wait_for_midnight()
	
def import_birthdays(): #Unused right now
	"""Import birthday"""
	print "importing birthdays"
	
def create_indicator():
	ind.set_status (appindicator.STATUS_PASSIVE)
	ind.set_attention_icon ("indicator-birthday-aware-attention")
	ind.set_menu(menu)

def run_main():
	create_indicator()
	print "waiting 30seconds, so people don't miss the notification"
	time.sleep(30) #wait for 30 seconds after starting up the program
	read_the_birthday_file()# how often should we read this file? we don't want it to be annyoing but people might miss it if they are running a full screen program
	gtk.main()

def main():
	return 0
	
if __name__ == '__main__':
	#NOTE I do not fully understand how this works
	sleep_thread = threading.Thread(target=wait_for_midnight, args=())
	main_thread = threading.Thread(target=run_main, args=())
	sleep_thread.start()
	main_thread.start()
	sleep_thread.join()
	main_thread.join()
